﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Threading.Tasks;
using K9Nano.Domain;
using Dapper;

namespace K9Nano.DapperExtensions
{
    public class DapperReadOnlyRepository : IReadOnlyRepository
    {
        protected readonly IReadOnlyDbConnectionFactory ReadOnlyDbConnectionFactory;

        public DapperReadOnlyRepository(IReadOnlyDbConnectionFactory readOnlyDbConnectionFactory)
        {
            this.ReadOnlyDbConnectionFactory = readOnlyDbConnectionFactory;
        }

        protected IDbConnection Connection => ReadOnlyDbConnectionFactory.ReadOnlyDbConnection;

        public virtual T ExecuteScalar<T>(string sql, object param = null, IDbTransaction transaction = null, int? commandTimeout = null,
            CommandType? commandType = null)
        {
            sql = ReadOnlyDbConnectionFactory.PrepareSharedSql(sql); 
            return Connection.ExecuteScalar<T>(sql, param, transaction, commandTimeout, commandType);
        }

        public virtual IDataReader ExecuteReader(string sql, object param = null, IDbTransaction transaction = null,
            int? commandTimeout = null, CommandType? commandType = null)
        {
            sql = ReadOnlyDbConnectionFactory.PrepareSharedSql(sql); 
            return Connection.ExecuteReader(sql, param, transaction, commandTimeout, commandType);
        }

        public virtual IEnumerable<T> Query<T>(string sql, object param = null, IDbTransaction transaction = null, bool buffered = true,
            int? commandTimeout = null, CommandType? commandType = null)
        {
            sql = ReadOnlyDbConnectionFactory.PrepareSharedSql(sql); 
            return Connection.Query<T>(sql, param, transaction, buffered, commandTimeout, commandType);
        }

        public virtual T QueryFirst<T>(string sql, object param = null, IDbTransaction transaction = null, int? commandTimeout = null,
            CommandType? commandType = null)
        {
            sql = ReadOnlyDbConnectionFactory.PrepareSharedSql(sql); 
            return Connection.QueryFirst<T>(sql, param, transaction, commandTimeout, commandType);
        }

        public virtual T QueryFirstOrDefault<T>(string sql, object param = null, IDbTransaction transaction = null, int? commandTimeout = null,
            CommandType? commandType = null)
        {
            sql = ReadOnlyDbConnectionFactory.PrepareSharedSql(sql); 
            return Connection.QueryFirstOrDefault<T>(sql, param, transaction, commandTimeout, commandType);
        }

        public virtual T QuerySingle<T>(string sql, object param = null, IDbTransaction transaction = null, int? commandTimeout = null,
            CommandType? commandType = null)
        {
            sql = ReadOnlyDbConnectionFactory.PrepareSharedSql(sql); 
            return Connection.QuerySingle<T>(sql, param, transaction, commandTimeout, commandType);
        }

        public virtual T QuerySingleOrDefault<T>(string sql, object param = null, IDbTransaction transaction = null,
            int? commandTimeout = null, CommandType? commandType = null)
        {
            sql = ReadOnlyDbConnectionFactory.PrepareSharedSql(sql); 
            return Connection.QuerySingleOrDefault<T>(sql, param, transaction, commandTimeout, commandType);
        }

        public virtual IEnumerable<TReturn> Query<TFirst, TSecond, TReturn>(string sql, Func<TFirst, TSecond, TReturn> map, object param = null,
            IDbTransaction transaction = null, bool buffered = true, string splitOn = "Id", int? commandTimeout = null,
            CommandType? commandType = null)
        {
            sql = ReadOnlyDbConnectionFactory.PrepareSharedSql(sql);
            return Connection.Query<TFirst, TSecond, TReturn>(sql, map, param, transaction, buffered, splitOn, commandTimeout, commandType);
        }

        public virtual IEnumerable<TReturn> Query<TFirst, TSecond, TThird, TReturn>(string sql, Func<TFirst, TSecond, TThird, TReturn> map, object param = null,
            IDbTransaction transaction = null, bool buffered = true, string splitOn = "Id", int? commandTimeout = null,
            CommandType? commandType = null)
        {
            sql = ReadOnlyDbConnectionFactory.PrepareSharedSql(sql); 
            return Connection.Query<TFirst, TSecond, TThird, TReturn>(sql, map, param, transaction, buffered, splitOn, commandTimeout, commandType);
        }

        public virtual IEnumerable<TReturn> Query<TFirst, TSecond, TThird, TFourth, TReturn>(string sql, Func<TFirst, TSecond, TThird, TFourth, TReturn> map, object param = null,
            IDbTransaction transaction = null, bool buffered = true, string splitOn = "Id", int? commandTimeout = null,
            CommandType? commandType = null)
        {
            sql = ReadOnlyDbConnectionFactory.PrepareSharedSql(sql); 
            return Connection.Query<TFirst, TSecond, TThird, TFourth, TReturn>(sql, map, param, transaction, buffered, splitOn, commandTimeout, commandType);
        }

        public virtual IEnumerable<TReturn> Query<TFirst, TSecond, TThird, TFourth, TFifth, TReturn>(string sql, Func<TFirst, TSecond, TThird, TFourth, TFifth, TReturn> map, object param = null,
            IDbTransaction transaction = null, bool buffered = true, string splitOn = "Id", int? commandTimeout = null,
            CommandType? commandType = null)
        {
            sql = ReadOnlyDbConnectionFactory.PrepareSharedSql(sql); 
            return Connection.Query<TFirst, TSecond, TThird, TFourth, TFifth, TReturn>(sql, map, param, transaction, buffered, splitOn, commandTimeout, commandType);
        }

        public virtual IEnumerable<TReturn> Query<TFirst, TSecond, TThird, TFourth, TFifth, TSixth, TReturn>(string sql, Func<TFirst, TSecond, TThird, TFourth, TFifth, TSixth, TReturn> map, object param = null,
            IDbTransaction transaction = null, bool buffered = true, string splitOn = "Id", int? commandTimeout = null,
            CommandType? commandType = null)
        {
            sql = ReadOnlyDbConnectionFactory.PrepareSharedSql(sql); 
            return Connection.Query(sql, map, param, transaction, buffered, splitOn, commandTimeout, commandType);
        }

        public virtual IEnumerable<TReturn> Query<TFirst, TSecond, TThird, TFourth, TFifth, TSixth, TSeventh, TReturn>(string sql, Func<TFirst, TSecond, TThird, TFourth, TFifth, TSixth, TSeventh, TReturn> map,
            object param = null, IDbTransaction transaction = null, bool buffered = true, string splitOn = "Id",
            int? commandTimeout = null, CommandType? commandType = null)
        {
            sql = ReadOnlyDbConnectionFactory.PrepareSharedSql(sql); 
            return Connection.Query(sql, map, param, transaction, buffered, splitOn, commandTimeout, commandType);
        }

        public virtual IEnumerable<TReturn> Query<TReturn>(string sql, Type[] types, Func<object[], TReturn> map, object param = null, IDbTransaction transaction = null,
            bool buffered = true, string splitOn = "Id", int? commandTimeout = null, CommandType? commandType = null)
        {
            sql = ReadOnlyDbConnectionFactory.PrepareSharedSql(sql);
            return Connection.Query<TReturn>(sql, types, map, param, transaction, buffered, splitOn, commandTimeout, commandType);
        }

        public virtual Task<IEnumerable<T>> QueryAsync<T>(string sql, object param = null, IDbTransaction transaction = null, int? commandTimeout = null,
            CommandType? commandType = null)
        {
            sql = ReadOnlyDbConnectionFactory.PrepareSharedSql(sql); 
            return Connection.QueryAsync<T>(sql, param, transaction, commandTimeout, commandType);
        }

        public virtual Task<T> QueryFirstAsync<T>(string sql, object param = null, IDbTransaction transaction = null, int? commandTimeout = null,
            CommandType? commandType = null)
        {
            sql = ReadOnlyDbConnectionFactory.PrepareSharedSql(sql); 
            return Connection.QueryFirstAsync<T>(sql, param, transaction, commandTimeout, commandType);
        }

        public virtual Task<T> QueryFirstOrDefaultAsync<T>(string sql, object param = null, IDbTransaction transaction = null,
            int? commandTimeout = null, CommandType? commandType = null)
        {
            sql = ReadOnlyDbConnectionFactory.PrepareSharedSql(sql); 
            return Connection.QueryFirstOrDefaultAsync<T>(sql, param, transaction, commandTimeout, commandType);
        }

        public virtual Task<T> QuerySingleAsync<T>(string sql, object param = null, IDbTransaction transaction = null, int? commandTimeout = null,
            CommandType? commandType = null)
        {
            sql = ReadOnlyDbConnectionFactory.PrepareSharedSql(sql); 
            return Connection.QuerySingleAsync<T>(sql, param, transaction, commandTimeout, commandType);
        }

        public virtual Task<T> QuerySingleOrDefaultAsync<T>(string sql, object param = null, IDbTransaction transaction = null,
            int? commandTimeout = null, CommandType? commandType = null)
        {
            sql = ReadOnlyDbConnectionFactory.PrepareSharedSql(sql); 
            return Connection.QuerySingleOrDefaultAsync<T>(sql, param, transaction, commandTimeout, commandType);
        }

        public virtual Task<IEnumerable<TReturn>> QueryAsync<TFirst, TSecond, TReturn>(string sql, Func<TFirst, TSecond, TReturn> map, object param = null, IDbTransaction transaction = null,
            bool buffered = true, string splitOn = "Id", int? commandTimeout = null, CommandType? commandType = null)
        {
            sql = ReadOnlyDbConnectionFactory.PrepareSharedSql(sql);
            return Connection.QueryAsync(sql, map, param, transaction, buffered, splitOn, commandTimeout, commandType);
        }

        public virtual Task<IEnumerable<TReturn>> QueryAsync<TFirst, TSecond, TThird, TReturn>(string sql, Func<TFirst, TSecond, TThird, TReturn> map, object param = null,
            IDbTransaction transaction = null, bool buffered = true, string splitOn = "Id", int? commandTimeout = null,
            CommandType? commandType = null)
        {
            sql = ReadOnlyDbConnectionFactory.PrepareSharedSql(sql);
            return Connection.QueryAsync(sql, map, param, transaction, buffered, splitOn, commandTimeout, commandType);
        }

        public virtual Task<IEnumerable<TReturn>> QueryAsync<TFirst, TSecond, TThird, TFourth, TReturn>(string sql, Func<TFirst, TSecond, TThird, TFourth, TReturn> map, object param = null,
            IDbTransaction transaction = null, bool buffered = true, string splitOn = "Id", int? commandTimeout = null,
            CommandType? commandType = null)
        {
            sql = ReadOnlyDbConnectionFactory.PrepareSharedSql(sql);
            return Connection.QueryAsync(sql, map, param, transaction, buffered, splitOn, commandTimeout, commandType);
        }

        public virtual Task<IEnumerable<TReturn>> QueryAsync<TFirst, TSecond, TThird, TFourth, TFifth, TReturn>(string sql, Func<TFirst, TSecond, TThird, TFourth, TFifth, TReturn> map, object param = null,
            IDbTransaction transaction = null, bool buffered = true, string splitOn = "Id", int? commandTimeout = null,
            CommandType? commandType = null)
        {
            sql = ReadOnlyDbConnectionFactory.PrepareSharedSql(sql);
            return Connection.QueryAsync(sql, map, param, transaction, buffered, splitOn, commandTimeout, commandType);
        }

        public virtual Task<IEnumerable<TReturn>> QueryAsync<TFirst, TSecond, TThird, TFourth, TFifth, TSixth, TReturn>(string sql, Func<TFirst, TSecond, TThird, TFourth, TFifth, TSixth, TReturn> map, object param = null,
            IDbTransaction transaction = null, bool buffered = true, string splitOn = "Id", int? commandTimeout = null,
            CommandType? commandType = null)
        {
            sql = ReadOnlyDbConnectionFactory.PrepareSharedSql(sql); 
            return Connection.QueryAsync(sql, map, param, transaction, buffered, splitOn, commandTimeout, commandType);
        }

        public virtual Task<IEnumerable<TReturn>> QueryAsync<TFirst, TSecond, TThird, TFourth, TFifth, TSixth, TSeventh, TReturn>(string sql, Func<TFirst, TSecond, TThird, TFourth, TFifth, TSixth, TSeventh, TReturn> map,
            object param = null, IDbTransaction transaction = null, bool buffered = true, string splitOn = "Id",
            int? commandTimeout = null, CommandType? commandType = null)
        {
            sql = ReadOnlyDbConnectionFactory.PrepareSharedSql(sql);
            return Connection.QueryAsync(sql, map, param, transaction, buffered, splitOn, commandTimeout, commandType);
        }

        public virtual Task<IEnumerable<TReturn>> QueryAsync<TReturn>(string sql, Type[] types, Func<object[], TReturn> map, object param = null, IDbTransaction transaction = null,
            bool buffered = true, string splitOn = "Id", int? commandTimeout = null, CommandType? commandType = null)
        {
            sql = ReadOnlyDbConnectionFactory.PrepareSharedSql(sql); 
            return Connection.QueryAsync<TReturn>(sql, types, map, param, transaction, buffered, splitOn,
               commandTimeout, CommandType.Text);
        }

        public virtual Task<IDataReader> ExecuteReaderAsync(string sql, object param = null, IDbTransaction transaction = null, int? commandTimeout = null,
            CommandType? commandType = null)
        {
            sql = ReadOnlyDbConnectionFactory.PrepareSharedSql(sql); 
            return Connection.ExecuteReaderAsync(sql, param, transaction, commandTimeout, commandType);
        }

        public virtual Task<T> ExecuteScalarAsync<T>(string sql, object param = null, IDbTransaction transaction = null,
            int? commandTimeout = null, CommandType? commandType = null)
        {
            sql = ReadOnlyDbConnectionFactory.PrepareSharedSql(sql);
            return Connection.ExecuteScalarAsync<T>(sql, param, transaction, commandTimeout, commandType);
        }
    }
}